#include "defines.h"
#include "kozos.h"
#include "lib.h"
#include "consdrv.h"
#include "alarm.h"

#define RECV_BUFFER_SIZE 32

static int send_use(kz_msgbox_id_t mb)
{
    consdrv_cmd_header *p;
    int ret;

    p = kz_kmalloc(sizeof(consdrv_cmd_header));
    p->cmd = CONSDRV_CMD_USE;
    p->mb = mb;
    send_to_cons(sizeof(consdrv_cmd_header), (char *)p);
    kz_recv(mb, &ret, NULL, 0);

    return ret;
}

static int send_close(kz_msgbox_id_t mb)
{
    consdrv_cmd_header *p;
    int ret;
    char *q;

    p = kz_kmalloc(sizeof(consdrv_cmd_header));
    p->cmd = CONSDRV_CMD_CLOSE;
    p->mb = mb;
    send_to_cons(sizeof(consdrv_cmd_header), (char *)p);
    do {
        kz_recv(mb, &ret, &q, 0);
    } while (q[0] != 'c');

    return ret;
}

// puts, putval, putxvalの対応物
static void send_write(const char *str)
{
    consdrv_cmd_header *p;
    int i, len;

    len = strlen(str);
    for (i = 0; i < len; i++) {
        p = kz_kmalloc(sizeof(consdrv_cmd_header));
        p->cmd = CONSDRV_CMD_WRITE;
        p->car = str[i];
        send_to_cons(sizeof(consdrv_cmd_header), (char *)p);
    }
}

static void send_val(uint32 value, int column)
{
    char buf[11];
    int len;

    len = ltos(value, buf);

    while (column > len) {
        send_write("0");
        column--;
    }

    send_write(buf);
}

static void send_xval(uint32 value, int column)
{
    char buf[9];
    int len;

    len = xtos(value, buf);

    while (column > len) {
        send_write("0");
        column--;
    }

    send_write(buf);
}

static char *getarg(char **pp)
{
    char *p = *pp;
    char *q;

    while (*p == ' ') p++;
    if (*p == '\0') return NULL;
    q = p;
    while (*p != ' ' && *p != '\0') p++;
    if (*p == ' ') *p++ = '\0';

    *pp = p;
    return q;
}

static const char *COMMANDS[] = {"echo", "exit", "help", "ps", "reset", "resource", "sleep", "time", NULL};

int command_main(int argc, char *argv[])
{
    int c, r;
    kz_msgbox_id_t msgbox_in;
    char *recv_buf;
    int recv_len;

    recv_buf = kz_kmalloc(RECV_BUFFER_SIZE);
    msgbox_in = kz_getmsgbox();
    r = send_use(msgbox_in);
    if (!r) {
        send_write("command: cannot use console\n");
        return -1;
    }

    while (1) {
        char *p, *q;

        send_write("command> ");

        // コマンド行を得る
        recv_len = 0;
        while (1) {
            kz_recv(msgbox_in, &c, NULL, 0);

            if (c == '\r') {
                send_write("\n");
                recv_buf[recv_len++] = '\0';
                break;

            } else if (c == '\b') {
                if (recv_len) {
                    send_write("\b \b");
                    recv_len--;
                }

            } else if (c == '\t') {
                ;

            } else if (recv_len < RECV_BUFFER_SIZE - 1) {
                char a[2] = {(char)c, '\0'};
                send_write(a);
                recv_buf[recv_len++] = (char)c;
            }
        }

        // コマンド処理
        p = recv_buf;
        q = getarg(&p);
        if (!q) continue;

        if (!strcmp(q, "echo")) {
            q = getarg(&p);
            if (q) {
                send_write(q);
                while (1) {
                    q = getarg(&p);
                    if (!q) break;
                    send_write(" ");
                    send_write(q);
                }
            }
            send_write("\n");

        } else if (!strcmp(q, "sleep")) {
            int t;
#define SLEEP_MAX   100
            if ((q = getarg(&p)) && is_num(q) && (t = atoi(q)) >= 0 && t <= SLEEP_MAX)
            {
                kz_msgbox_id_t mb = kz_getmsgbox();
                alarm_s(t, mb, 0, NULL);
                kz_recv(mb, NULL, NULL, 0);
                kz_freemsgbox(mb);

            } else {
                send_write("invalid time. (0-");
                send_val(SLEEP_MAX, 0);
                send_write(")\n");
            }

        } else if (!strcmp(q, "help")) {
            const char **p;
            for(p = COMMANDS; *p; p++) {
                send_write(*p);
                send_write("\n");
            }

        } else if (!strcmp(q, "ps")) {
            int size, i;
            kz_thread_info_t *info, *q;

            send_write("NAME PRIORITY STACKSIZE FLAGS\n");

            info = kz_getthreads(&size);

            q = info;  /* 添字アクセスだと__mulsi3が必要になる */
            for (i = 0; i < size; i++, q++) {
                send_write(q->name);
                send_write(" ");
                send_val(q->priority, 0);
                send_write(" 0x");
                send_xval(q->stacksize, 0);
                send_write(" 0x");
                send_xval(q->flags, 4);
                send_write("\n");
            }

            kz_kmfree(info);

        } else if (!strcmp(q, "exit")) {
            break;

        } else if (!strcmp(q, "reset")) {
            kz_reset();

        } else if (!strcmp(q, "resource")) {
            kz_resource();

        } else if (!strcmp(q, "time")) {
            uint32 H;
            uint16 M, S;

            H = system_time();
            H = div3216(H, 60, &S);
            H = div3216(H, 60, &M);

            send_val(H, 2);
            send_write(":");
            send_val(M, 2);
            send_write(":");
            send_val(S, 2);
            send_write("\n");

        } else {
            send_write("unknown command.\n");
        }
    }

    send_close(msgbox_in);
    kz_freemsgbox(msgbox_in);
    kz_kmfree(recv_buf);

    return 0;
}
